package com.dc.dbus.session.bean;

import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.dc.dbus.session.exception.RemoteException;
import com.dc.dbus.session.exception.RemoteExceptionWrapper;
import com.dc.dbus.session.exception.RemoteTimeOutException;


public class FutureResult<T> implements Future<T> {

	private volatile boolean completed;
	private volatile Object result;
	private volatile Integer errorCode;
	private volatile boolean timeout;

	public FutureResult() {
		super();
	}
	
	public FutureResult(T result) {
		this.completed = true;
		this.result = result;
	}

	public boolean isCancelled() {
		return false;
	}

	public boolean isDone() {
		return this.completed;
	}

	@SuppressWarnings("unchecked")
	private T getResult() {
		if (this.errorCode != null) {
			if (this.result == null) {
				throw new RemoteException(this.errorCode);
			} else {
				throw new RemoteExceptionWrapper((Throwable) this.result);
			}
		}
		if (this.timeout) {
			throw new RemoteTimeOutException();
		}
		return (T) this.result;
	}

	public synchronized T get() {
		while (!this.completed) {
			try {
				wait();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
		return getResult();
	}

	public synchronized T get(final long timeout, final TimeUnit unit) throws TimeoutException {

		final long msecs = unit.toMillis(timeout);
		final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis();
		long waitTime = msecs;
		if (this.completed) {
			return getResult();
		} else if (waitTime <= 0) {
			throw new TimeoutException();
		} else {
			for (;;) {
				try {
					wait(waitTime);
				} catch (InterruptedException e) {
					throw new RuntimeException(e);
				}
				if (this.completed) {
					return getResult();
				} else {
					waitTime = msecs - (System.currentTimeMillis() - startTime);
					if (waitTime <= 0) {
						throw new TimeoutException();
					}
				}
			}
		}
	}

	public boolean completed(final T result) {
		synchronized (this) {
			if (this.completed) {
				return false;
			}
			this.completed = true;
			this.result = result;
			notifyAll();
		}
		return true;
	}

	public boolean failed(final int errorCode) {
		synchronized (this) {
			if (this.completed) {
				return false;
			}
			this.completed = true;
			this.errorCode = errorCode;
			notifyAll();
		}
		return true;
	}
	
	public boolean failedWithRemoteException(final int errorCode, final Throwable throwable) {
		synchronized (this) {
			if (this.completed) {
				return false;
			}
			this.completed = true;
			this.errorCode = errorCode;
			this.result = throwable;
			notifyAll();
		}
		return true;
	}
	
	public boolean failedByTimeOut() {
		synchronized (this) {
			if (this.completed) {
				return false;
			}
			this.completed = true;
			this.timeout = true;
			notifyAll();
		}
		return true;
	}

	public boolean cancel(final boolean mayInterruptIfRunning) {
		return false;
	}

}
